#include "zos_config.h"

#ifdef BOARD_USING_LCD_ILI9341

#include "lcd.h"
#include "lcd_font.h"
#include "zos.h"

#define LCD_W 240
#define LCD_H 320

#define LCD_PWR_PIN PIN_13
#define LCD_DC_PIN  PIN_20
#define LCD_RES_PIN PIN_21
#define LCD_CLEAR_SEND_NUMBER 76800

#define USE_HORIZONTAL 0  //设置横屏或者竖屏显示 0或1为竖屏 2或3为横屏

zos_uint16_t BACK_COLOR = WHITE, FORE_COLOR = BLACK;

//向SPI总线传输一个8位数据
void  spi_write_data(zos_uint8_t data)
{
	//使能SPI CS管脚
	zos_spi_cs_enable(ZOS_SPI_BUS_LCD);

	zos_spi_write(ZOS_SPI_BUS_LCD,&data,1,10);

	//使能SPI CS管脚
	zos_spi_cs_disable(ZOS_SPI_BUS_LCD);
}

//向液晶屏写一个8位指令
void lcd_write_cmd(zos_uint8_t index)
{
	zos_pin_write(LCD_DC_PIN, PIN_LOW);
	spi_write_data(index);
	zos_pin_write(LCD_DC_PIN, PIN_HIGH);
}

//向液晶屏写一个8位数据
void lcd_write_data(zos_uint8_t data)
{
    zos_pin_write(LCD_DC_PIN, PIN_HIGH);
    spi_write_data(data);
}
//向液晶屏写一个16位数据
void lcd_write_half_word(zos_uint16_t data)
{
    zos_uint8_t data0[2];
    zos_uint16_t str;
    data0[0]=data>>8;
    data0[1]=(zos_uint8_t)data;
    str=data0[0]|data0[1]<<8;
	zos_pin_write(LCD_DC_PIN, PIN_HIGH);
    	//使能SPI CS管脚
	zos_spi_cs_enable(ZOS_SPI_BUS_LCD);
    
	zos_spi_write_16_bit(ZOS_SPI_BUS_LCD,&str);

	//使能SPI CS管脚
	zos_spi_cs_disable(ZOS_SPI_BUS_LCD);
}

static void lcd_gpio_init(void)
{
    ZOS_SPI_CONFIG spi_cfg;

	zos_pin_mode(LCD_DC_PIN,PIN_MODE_OUTPUT);
	zos_pin_mode(LCD_RES_PIN,PIN_MODE_OUTPUT);
    zos_pin_mode(LCD_PWR_PIN,PIN_MODE_OUTPUT);

	spi_cfg.clk_div=ZOS_SPI_CLK_DIV2;
	spi_cfg.clk_mode=ZOS_SPI_CLK_MODE3;
	zos_spi_init(ZOS_SPI_BUS_LCD,&spi_cfg);

    zos_pin_write(LCD_PWR_PIN, PIN_LOW);

    zos_pin_write(LCD_RES_PIN, PIN_LOW);
    //wait at least 100ms for reset
    zos_task_delay(100);
    zos_pin_write(LCD_RES_PIN, PIN_HIGH);
}

void lcd_reset(void)
{
    zos_pin_write(LCD_RES_PIN,PIN_LOW);
    zos_task_mdelay(100);
    zos_pin_write(LCD_RES_PIN,PIN_HIGH);
    zos_task_mdelay(50);
}
zos_err_t lcd_init(void)
{
	lcd_gpio_init();
	lcd_reset();
	//LCD Init For 1.44Inch LCD Panel with ILI9341R.
	lcd_write_cmd(0x11);//Sleep exit
	zos_task_mdelay(120);

    //ILI9341R Frame Rate
	lcd_write_cmd(0xCF);
	lcd_write_data(0x00);
	lcd_write_data(0xD9);
	lcd_write_data(0X30);

	lcd_write_cmd(0xED);
	lcd_write_data(0x64);
	lcd_write_data(0x03);
	lcd_write_data(0X12);
	lcd_write_data(0X81);

	lcd_write_cmd(0xE8);
	lcd_write_data(0x85);
	lcd_write_data(0x10);
	lcd_write_data(0x78);

	lcd_write_cmd(0xCB);
	lcd_write_data(0x39);
	lcd_write_data(0x2C);
	lcd_write_data(0x00);
	lcd_write_data(0x34);
	lcd_write_data(0x02);

	lcd_write_cmd(0xF7);
	lcd_write_data(0x20);

	lcd_write_cmd(0xEA);
	lcd_write_data(0x00);
	lcd_write_data(0x00);

	lcd_write_cmd(0xC0); //Power control
	lcd_write_data(0x21); //VRH[5:0]

	lcd_write_cmd(0xC1); //Power control
	lcd_write_data(0x12); //SAP[2:0];BT[3:0]

	lcd_write_cmd(0xC5); //VCM control
	lcd_write_data(0x32);
	lcd_write_data(0x3c);

	lcd_write_cmd(0xC7); //VCM control
	lcd_write_data(0xC1);

	lcd_write_cmd(0x36); // Memory Access Control
	if(USE_HORIZONTAL==0)lcd_write_data(0x08);
	else if(USE_HORIZONTAL==1)lcd_write_data(0xC8);
	else if(USE_HORIZONTAL==2)lcd_write_data(0x78);
	else lcd_write_data(0xA8);

	lcd_write_cmd(0x3A);   
	lcd_write_data(0x55); 

	lcd_write_cmd(0xB1);
	lcd_write_data(0x00);
	lcd_write_data(0x18);

	lcd_write_cmd(0xB6); // Display Function Control
	lcd_write_data(0x0A);
	lcd_write_data(0xA2);

	lcd_write_cmd(0xF2); // 3Gamma Function Disable
	lcd_write_data(0x00);

	lcd_write_cmd(0x26); //Gamma curve selected
	lcd_write_data(0x01);

	lcd_write_cmd(0xE0); //Set Gamma
	lcd_write_data(0x0F);
	lcd_write_data(0x20);
	lcd_write_data(0x1E);
	lcd_write_data(0x09);
	lcd_write_data(0x12);
	lcd_write_data(0x0B);
	lcd_write_data(0x50);
	lcd_write_data(0xBA);
	lcd_write_data(0x44);
	lcd_write_data(0x09);
	lcd_write_data(0x14);
	lcd_write_data(0x05);
	lcd_write_data(0x23);
	lcd_write_data(0x21);
	lcd_write_data(0x00);

	lcd_write_cmd(0XE1); //Set Gamma
	lcd_write_data(0x00);
	lcd_write_data(0x19);
	lcd_write_data(0x19);
	lcd_write_data(0x00);
	lcd_write_data(0x12);
	lcd_write_data(0x07);
	lcd_write_data(0x2D);
	lcd_write_data(0x28);
	lcd_write_data(0x3F);
	lcd_write_data(0x02);
	lcd_write_data(0x0A);
	lcd_write_data(0x08);
	lcd_write_data(0x25);
	lcd_write_data(0x2D);
	lcd_write_data(0x0F);

	
    lcd_clear(WHITE);
	/* display on */
	zos_pin_write(LCD_PWR_PIN, PIN_HIGH);
	lcd_write_cmd(0x29);

	return ZOS_EOK;
}

/**
 * Set background color and foreground color
 *
 * @param   back    background color
 * @param   fore    fore color
 *
 * @return  void
 */
void lcd_set_color(zos_uint16_t back, zos_uint16_t fore)
{
    BACK_COLOR = back;
    FORE_COLOR = fore;
}

void lcd_display_on(void)
{
    zos_pin_write(LCD_PWR_PIN, PIN_HIGH);
}

void lcd_display_off(void)
{
    zos_pin_write(LCD_PWR_PIN, PIN_LOW);
}

/* lcd enter the minimum power consumption mode and backlight off. */
void lcd_enter_sleep(void)
{
    zos_pin_write(LCD_PWR_PIN, PIN_LOW);
    zos_task_mdelay(5);
    lcd_write_cmd(0x10);
}
/* lcd turn off sleep mode and backlight on. */
void lcd_exit_sleep(void)
{
    zos_pin_write(LCD_PWR_PIN, PIN_HIGH);
    zos_task_mdelay(5);
    lcd_write_cmd(0x11);
    zos_task_mdelay(120);
}

/**
 * Set drawing area
 *
 * @param   x1      start of x position
 * @param   y1      start of y position
 * @param   x2      end of x position
 * @param   y2      end of y position
 *
 * @return  void
 */
void lcd_address_set(zos_uint16_t x1, zos_uint16_t y1, zos_uint16_t x2, zos_uint16_t y2)
{
	lcd_write_cmd(0x2a);
	lcd_write_half_word(x1);
	lcd_write_half_word(x2);

	lcd_write_cmd(0x2b);
	lcd_write_half_word(y1);
	lcd_write_half_word(y2);
	
	lcd_write_cmd(0x2c);
}


/**
 * clear the lcd.
 *
 * @param   color       Fill color
 *
 * @return  void
 */
void lcd_clear(zos_uint16_t color)
{
    unsigned int i,m;
    zos_uint8_t data0[2];
    zos_uint16_t str;
    data0[0]=color>>8;
    data0[1]=(zos_uint8_t)color;
    str=data0[0]|data0[1]<<8;
    lcd_address_set(0,0,LCD_W-1,LCD_H-1);

    zos_pin_write(LCD_DC_PIN, PIN_HIGH);
    //使能SPI CS管脚
    zos_spi_cs_enable(ZOS_SPI_BUS_LCD);
    for(i=0;i<LCD_W;i++)
        for(m=0;m<LCD_H;m++)
        {	
            zos_spi_write_16_bit(ZOS_SPI_BUS_LCD,&str);
        }

    //使能SPI CS管脚
    zos_spi_cs_disable(ZOS_SPI_BUS_LCD); 
}

/**
 * display a point on the lcd.
 *
 * @param   x   x position
 * @param   y   y position
 *
 * @return  void
 */
void lcd_draw_point(zos_uint16_t x, zos_uint16_t y,zos_uint16_t color)
{
    lcd_address_set(x, y, x, y);
    lcd_write_half_word(color);
}

/**
 * full color on the lcd.
 *
 * @param   x_start     start of x position
 * @param   y_start     start of y position
 * @param   x_end       end of x position
 * @param   y_end       end of y position
 * @param   color       Fill color
 *
 * @return  void
 */
void lcd_fill(zos_uint16_t x_start, zos_uint16_t y_start, zos_uint16_t x_end, zos_uint16_t y_end, zos_uint16_t color)
{
    zos_uint16_t i = 0, j = 0;
    zos_uint8_t data0[2];
    zos_uint16_t str;
    data0[0]=color>>8;
    data0[1]=(zos_uint8_t)color;
    str=data0[0]|data0[1]<<8;
    lcd_address_set(x_start, y_start, x_end, y_end);
    zos_spi_cs_enable(ZOS_SPI_BUS_LCD);
    for (i = y_start; i <= y_end; i++)
    {
        for (j = x_start; j <= x_end; j++)
            zos_spi_write_16_bit(ZOS_SPI_BUS_LCD,&str);
    }

    //使能SPI CS管脚
    zos_spi_cs_disable(ZOS_SPI_BUS_LCD); 
}

/**
 * display a line on the lcd.
 *
 * @param   x1      x1 position
 * @param   y1      y1 position
 * @param   x2      x2 position
 * @param   y2      y2 position
 *
 * @return  void
 */
void lcd_draw_line(zos_uint16_t x1, zos_uint16_t y1, zos_uint16_t x2, zos_uint16_t y2,zos_uint16_t color)
{
    zos_uint16_t t;
    zos_uint16_t i = 0;
    int xerr = 0, yerr = 0, delta_x, delta_y, distance;
    int incx, incy, row, col;

    if (y1 == y2)
    {
        /* fast draw transverse line */
        lcd_address_set(x1, y1, x2, y2);

        zos_uint8_t line_buf[480] = {0};

        for (i = 0; i < (x2 - x1); i++)
        {
            line_buf[2 * i] = color >> 8;
            line_buf[2 * i + 1] = color;
        }

        zos_pin_write(LCD_DC_PIN, PIN_HIGH);
        zos_spi_cs_enable(ZOS_SPI_BUS_LCD);
        zos_spi_write(ZOS_SPI_BUS_LCD,line_buf,(x2 - x1)*2,10);
        zos_spi_cs_disable(ZOS_SPI_BUS_LCD);
        return ;
    }

    delta_x = x2 - x1;
    delta_y = y2 - y1;
    row = x1;
    col = y1;
    if (delta_x > 0)incx = 1;
    else if (delta_x == 0)incx = 0;
    else
    {
        incx = -1;
        delta_x = -delta_x;
    }
    if (delta_y > 0)incy = 1;
    else if (delta_y == 0)incy = 0;
    else
    {
        incy = -1;
        delta_y = -delta_y;
    }
    if (delta_x > delta_y)distance = delta_x;
    else distance = delta_y;
    for (t = 0; t <= distance + 1; t++)
    {
        lcd_draw_point(row, col,color);
        xerr += delta_x ;
        yerr += delta_y ;
        if (xerr > distance)
        {
            xerr -= distance;
            row += incx;
        }
        if (yerr > distance)
        {
            yerr -= distance;
            col += incy;
        }
    }
}

/**
 * display a rectangle on the lcd.
 *
 * @param   x1      x1 position
 * @param   y1      y1 position
 * @param   x2      x2 position
 * @param   y2      y2 position
 *
 * @return  void
 */
void lcd_draw_rectangle(zos_uint16_t x1, zos_uint16_t y1, zos_uint16_t x2, zos_uint16_t y2,zos_uint16_t color)
{
    lcd_draw_line(x1, y1, x2, y1,color);
    lcd_draw_line(x1, y1, x1, y2,color);
    lcd_draw_line(x1, y2, x2, y2,color);
    lcd_draw_line(x2, y1, x2, y2,color);
}

/**
 * display a circle on the lcd.
 *
 * @param   x       x position of Center
 * @param   y       y position of Center
 * @param   r       radius
 *
 * @return  void
 */
void lcd_draw_circle(zos_uint16_t x0, zos_uint16_t y0, zos_uint8_t r,zos_uint16_t color)
{
    int a, b;
    int di;
    a = 0;
    b = r;
    di = 3 - (r << 1);
    while (a <= b)
    {
        lcd_draw_point(x0 - b, y0 - a,color);
        lcd_draw_point(x0 + b, y0 - a,color);
        lcd_draw_point(x0 - a, y0 + b,color);
        lcd_draw_point(x0 - b, y0 - a,color);
        lcd_draw_point(x0 - a, y0 - b,color);
        lcd_draw_point(x0 + b, y0 + a,color);
        lcd_draw_point(x0 + a, y0 - b,color);
        lcd_draw_point(x0 + a, y0 + b,color);
        lcd_draw_point(x0 - b, y0 + a,color);
        a++;
        //Bresenham
        if (di < 0)di += 4 * a + 6;
        else
        {
            di += 10 + 4 * (a - b);
            b--;
        }
        lcd_draw_point(x0 + a, y0 + b,color);
    }
}

static void lcd_show_char(zos_uint16_t x, zos_uint16_t y, zos_uint8_t data, zos_uint32_t size)
{
    zos_uint8_t temp;
    zos_uint8_t num = 0;;
    zos_uint8_t pos, t;
    zos_uint16_t colortemp = FORE_COLOR;
    zos_uint8_t *font_buf = ZOS_NULL;

    if (x > LCD_W - size / 2 || y > LCD_H - size)return;

    data = data - ' ';
#ifdef ASC2_1608
    if (size == 16)
    {
        lcd_address_set(x, y, x + size / 2 - 1, y + size - 1);//(x,y,x+8-1,y+16-1)

        font_buf = (zos_uint8_t *)zos_malloc(size * size);
        if (!font_buf)
        {
            /* fast show char */
            for (pos = 0; pos < size * (size / 2) / 8; pos++)
            {
                temp = asc2_1608[(zos_uint16_t)data * size * (size / 2) / 8 + pos];
                for (t = 0; t < 8; t++)
                {
                    if (temp & 0x80)colortemp = FORE_COLOR;
                    else colortemp = BACK_COLOR;
                    lcd_write_half_word(colortemp);
                    temp <<= 1;
                }
            }
        }
        else
        {
            for (pos = 0; pos < size * (size / 2) / 8; pos++)
            {
                temp = asc2_1608[(zos_uint16_t)data * size * (size / 2) / 8 + pos];
                for (t = 0; t < 8; t++)
                {
                    if (temp & 0x80)colortemp = FORE_COLOR;
                    else colortemp = BACK_COLOR;
                    font_buf[2 * (8 * pos + t)] = colortemp >> 8;
                    font_buf[2 * (8 * pos + t) + 1] = colortemp;
                    temp <<= 1;
                }
            }
            zos_pin_write(LCD_DC_PIN, PIN_HIGH);
            zos_spi_cs_enable(ZOS_SPI_BUS_LCD);
            zos_spi_write(ZOS_SPI_BUS_LCD,font_buf,size * size,10);
            zos_spi_cs_disable(ZOS_SPI_BUS_LCD);
            zos_free(font_buf);
        }
    }
    else
#endif

#ifdef ASC2_2412
        if (size == 24)
        {
            lcd_address_set(x, y, x + size / 2 - 1, y + size - 1);

            font_buf = (zos_uint8_t *)zos_malloc(size * size);
            if (!font_buf)
            {
                /* fast show char */
                for (pos = 0; pos < (size * 16) / 8; pos++)
                {
                    temp = asc2_2412[(zos_uint16_t)data * (size * 16) / 8 + pos];
                    if (pos % 2 == 0)
                    {
                        num = 8;
                    }
                    else
                    {
                        num = 4;
                    }

                    for (t = 0; t < num; t++)
                    {
                        if (temp & 0x80)colortemp = FORE_COLOR;
                        else colortemp = BACK_COLOR;
                        lcd_write_half_word(colortemp);
                        temp <<= 1;
                    }
                }
            }
            else
            {
                for (pos = 0; pos < (size * 16) / 8; pos++)
                {
                    temp = asc2_2412[(zos_uint16_t)data * (size * 16) / 8 + pos];
                    if (pos % 2 == 0)
                    {
                        num = 8;
                    }
                    else
                    {
                        num = 4;
                    }

                    for (t = 0; t < num; t++)
                    {
                        if (temp & 0x80)colortemp = FORE_COLOR;
                        else colortemp = BACK_COLOR;
                        if (num == 8)
                        {
                            font_buf[2 * (12 * (pos / 2) + t)] = colortemp >> 8;
                            font_buf[2 * (12 * (pos / 2) + t) + 1] = colortemp;
                        }
                        else
                        {
                            font_buf[2 * (8 + 12 * (pos / 2) + t)] = colortemp >> 8;
                            font_buf[2 * (8 + 12 * (pos / 2) + t) + 1] = colortemp;
                        }
                        temp <<= 1;
                    }
                }
                zos_pin_write(LCD_DC_PIN, PIN_HIGH);
                zos_spi_cs_enable(ZOS_SPI_BUS_LCD);
                zos_spi_write(ZOS_SPI_BUS_LCD,font_buf,size * size,10);
                zos_spi_cs_disable(ZOS_SPI_BUS_LCD);
                zos_free(font_buf);
            }
        }
        else
#endif

#ifdef ASC2_3216
            if (size == 32)
            {
                lcd_address_set(x, y, x + size / 2 - 1, y + size - 1);

                font_buf = (zos_uint8_t *)zos_malloc(size * size);
                if (!font_buf)
                {
                    /* fast show char */
                    for (pos = 0; pos < size * (size / 2) / 8; pos++)
                    {
                        temp = asc2_3216[(zos_uint16_t)data * size * (size / 2) / 8 + pos];
                        for (t = 0; t < 8; t++)
                        {
                            if (temp & 0x80)colortemp = FORE_COLOR;
                            else colortemp = BACK_COLOR;
                            lcd_write_half_word(colortemp);
                            temp <<= 1;
                        }
                    }
                }
                else
                {
                    for (pos = 0; pos < size * (size / 2) / 8; pos++)
                    {
                        temp = asc2_3216[(zos_uint16_t)data * size * (size / 2) / 8 + pos];
                        for (t = 0; t < 8; t++)
                        {
                            if (temp & 0x80)colortemp = FORE_COLOR;
                            else colortemp = BACK_COLOR;
                            font_buf[2 * (8 * pos + t)] = colortemp >> 8;
                            font_buf[2 * (8 * pos + t) + 1] = colortemp;
                            temp <<= 1;
                        }
                    }
                    zos_pin_write(LCD_DC_PIN, PIN_HIGH);
                    zos_spi_cs_enable(ZOS_SPI_BUS_LCD);
                    zos_spi_write(ZOS_SPI_BUS_LCD,font_buf,size * size,10);
                    zos_spi_cs_disable(ZOS_SPI_BUS_LCD);
                    zos_free(font_buf);
                }
            }
            else
#endif
            {
                zos_printf("[drv_lcd] There is no any define ASC2_1208 && ASC2_2412 && ASC2_2416 && ASC2_3216 !\r\n");
            }
}

/**
 * display the number on the lcd.
 *
 * @param   x       x position
 * @param   y       y position
 * @param   num     number
 * @param   len     length of number
 * @param   size    size of font
 *
 * @return  void
 */
void lcd_show_num(zos_uint16_t x, zos_uint16_t y, zos_uint32_t num, zos_uint8_t len, zos_uint32_t size)
{
    lcd_show_string(x, y, size, "%d", num);
}

/**
 * display the string on the lcd.
 *
 * @param   x       x position
 * @param   y       y position
 * @param   size    size of font
 * @param   p       the string to be display
 *
 * @return   0: display success
 *          -1: size of font is not support
 */
zos_err_t lcd_show_string(zos_uint16_t x, zos_uint16_t y, zos_uint32_t size, const char *fmt, ...)
{
#define LCD_STRING_BUF_LEN 128

    va_list args;
    zos_uint8_t buf[LCD_STRING_BUF_LEN] = {0};
    zos_uint8_t *p = ZOS_NULL;

    if (size != 16 && size != 24 && size != 32)
    {
        zos_printf("[drv_lcd] font size(%d) is not support!\r\n", size);
        return ZOS_ERROR;
    }

    va_start(args, fmt);
    vsnprintf((char *)buf, 100, (const char *)fmt, args);
    va_end(args);

    p = buf;
    while (*p != '\0')
    {
        if (x > LCD_W - size / 2)
        {
            x = 0;
            y += size;
        }
        if (y > LCD_H - size)
        {
            y = x = 0;
            lcd_clear(FORE_COLOR);
        }
        lcd_show_char(x, y, *p, size);
        x += size / 2;
        p++;
    }

    return ZOS_EOK;
}

/**
 * display the image on the lcd.
 *
 * @param   x       x position
 * @param   y       y position
 * @param   length  length of image
 * @param   wide    wide of image
 * @param   p       image
 *
 * @return   0: display success
 *          -1: the image is too large
 */
zos_err_t lcd_show_image(zos_uint16_t x, zos_uint16_t y, zos_uint16_t length, zos_uint16_t wide, const zos_uint8_t *p)
{
    ZOS_ASSERT(p);
	int i; 
	unsigned char picH,picL;
	lcd_clear(WHITE); //清屏  
	
	lcd_address_set(x,y,length-1,wide-1);	//坐标设置
	for(i=0;i<length*wide;i++)
	{	
		picL=*(p+i*2);	//数据低位在前
		picH=*(p+i*2+1);				
		lcd_write_half_word(picH<<8|picL);  						
	}	
   return ZOS_EOK;
}


#endif
